package com.sayimo.maker.commons.ali.pay;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.sayimo.framework.exception.ServiceException;
import com.sayimo.maker.commons.Constant;
import com.sayimo.maker.commons.utils.JSONUtil;
import com.sayimo.maker.commons.utils.PropertiesUtils;
import com.sayimo.maker.commons.utils.RequestUtils;

public class ALiPay {

	private static Log logger = LogFactory.getLog(ALiPay.class);
	
	/**
	 * 
	 * @MethodName aliPayOrder
	 * @Description 支付宝预支付
	 * @author wangyang
	 * @date 2017年2月9日 上午10:17:22
	 * @param _paramMap
	 * <p>
	 * 	goodsName 商品名称<br>
	 *  ordersNo 订单号<br>
	 *  payPrice 总金额<br>
	 *  <br>
	 *  
	 *  <strong>以“_”为开头的参数全部回传给支付宝，作为passback_params传输
	 *  </p>
	 * @return Map
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Map aliPayOrder(Map _paramMap) throws ServiceException {
		Map resultMap = new HashMap();
		
		try{
			logger.info("paramMap = " + JSONUtil.writeValueAsString(_paramMap));

			//公共参数
	        Map<String, String> param  = new HashMap<String, String>();
	        param.put("app_id", AlipayUtil.ALIPAY_APPID); //支付宝分配给开发者的应用ID
	        param.put("method", "alipay.trade.app.pay");
	        param.put("format", "json");
	        param.put("charset", "utf-8");
	        param.put("sign_type", "RSA2");
	        param.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
	        param.put("version", "1.0");
	        
	        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
			HttpServletRequest request = servletRequestAttributes.getRequest();
			
	        logger.info("支付宝回掉地址  = " + PropertiesUtils.getValue("SERVER_URL") + request.getContextPath()+"/orders/callbackalipay");
	        param.put("notify_url", PropertiesUtils.getValue("SERVER_URL") + request.getContextPath()+"/orders/callbackalipay");
	        
	        //业务参数
	        Map<String, String> pcont  = new HashMap<String, String>();
	        String subject= (String) _paramMap.get("goodsName");
	        String out_trade_no= (String) _paramMap.get("ordersNo");
	        String total_amount= String.valueOf(_paramMap.get("payPrice"));
	        
	        pcont.put("subject", subject); // 标题
	        
	        pcont.put("out_trade_no", out_trade_no); // 订单号
	        pcont.put("timeout_express", "30m"); // 订单失效时间
	        pcont.put("total_amount", total_amount); // 金额
	        pcont.put("seller_id", ""); // 收款支付宝用户ID。 如果该值为空，则默认为商户签约账号对应的支付宝用户ID
	        pcont.put("product_code", "QUICK_MSECURITY_PAY"); // 销售产品码，商家和支付宝签约的产品码，为固定值QUICK_MSECURITY_PAY
	        
	        // 支付宝回掉参数
	        {
		        Map<String, Object> passBackMap = new HashMap<>();
		        Set<String> keys = _paramMap.keySet();
		        for (String key : keys) {
					if (!key.startsWith("_")) {
						continue;
					}
					
					passBackMap.put(key, _paramMap.get(key));
				}
		        
		        if (MapUtils.isNotEmpty(passBackMap)) {
		        	String passBackParams = JSONUtil.writeValueAsString(passBackMap);
			        logger.info("passBackParams = " + passBackParams);
					String passback_params = URLEncoder.encode(passBackParams,"utf-8"); 
			        pcont.put("passback_params", passback_params);  //公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。支付宝会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
				}
	        }
	     
	        param.put("biz_content", JSON.toJSONString(pcont)); // 业务请求参数的集合，最大长度不限，除公共参数外所有请求参数都必须放在这个参数中传递，具体参照各产品快速接入文档
			
	        //对未签名原始字符串进行签名
	        String _param=AlipaySignature.getSignContent(param);
	        String rsaSign = AlipaySignature.rsaSign(_param, AlipayUtil.APP_PRIVATE_KEY, "utf-8","RSA2");
	        
	        param.put("sign", rsaSign);   //商户请求参数的签名串
	      
	        Map<String, String> payMap = new HashMap<String, String>();
	        payMap.put("orderStr", getSignEncodeUrl(param, true));
	        payMap.put("ordersNo", String.valueOf(_paramMap.get("ordersNo")));
	        if(String.valueOf(_paramMap.get("ordersNo")).substring(0, 3).equals("005")){
	        	payMap.put("consumerOrdersNo", String.valueOf(_paramMap.get("consumerOrdersNo")));
	        }
	        
	        logger.info(JSON.toJSONString(payMap));
	        resultMap.put(Constant.STATUS, Constant.STATUS_SUCCESS);
			resultMap.put(Constant.DATA, payMap);
			
		} catch (Exception e) {
			e.printStackTrace();
			throw new ServiceException(e);
		}
		
		
		return resultMap;
	}
	
	
	/**
	 * 返回签名编码拼接url
	 * 
	 * @param params
	 * @param isEncode
	 * @return
	 */
	private static String getSignEncodeUrl(Map<String, String> map, boolean isEncode) {
		String sign = map.get("sign");
		String encodedSign = "";
		if (map !=null && map.size() > 0) {
			map.remove("sign");
			List<String> keys = new ArrayList<String>(map.keySet());
			// key排序
			Collections.sort(keys);

			StringBuilder authInfo = new StringBuilder();

			boolean first = true;// 是否第一个
			for (String key: keys) {
				if (first) {
					first = false;
				} else {
					authInfo.append("&");
				}
				authInfo.append(key).append("=");
				if (isEncode) {
					try {
						authInfo.append(URLEncoder.encode(map.get(key), AlipayConstants.CHARSET_UTF8));
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
				} else {
					authInfo.append(map.get(key));
				}
			}

			try {
				encodedSign = authInfo.toString() + "&sign=" + URLEncoder.encode(sign, AlipayConstants.CHARSET_UTF8);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}

		return encodedSign.replaceAll("\\+", "%20");
	}
	
	/**
	 * 
	 * @MethodName AlipayTradeQuery
	 * @Description 查询订单是否支付成功
	 * 
	 * @author wangyang
	 * @date 2017年2月9日 上午10:17:22
	 * @param 
	 * @return Map
	 * @throws AlipayApiException 
	 */
	public static boolean validAlipayTradeQuery(String out_trade_no) throws AlipayApiException{
		AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
				AlipayUtil.ALIPAY_APPID,AlipayUtil.APP_PRIVATE_KEY,"json",AlipayConstants.CHARSET_UTF8,
				AlipayUtil.ALIPAY_PUBLIC_KEY,"RSA2");
		AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest(); // 统一收单线下交易查询
		 // 只需要传入业务参数
        Map<String, Object> param = new HashMap<>();
        param.put("out_trade_no", out_trade_no); // 商户订单号
		alipayRequest.setBizContent(JSON.toJSONString(param));
		boolean flag = false; // 查询状态
		
		AlipayTradeQueryResponse response = alipayClient.execute(alipayRequest);
		logger.info("response = " + JSONUtil.writeValueAsString(response));
		if(response.isSuccess()){
			if ("10000".equals(response.getCode())) {
				System.out.println(response.getCode());
				if("TRADE_SUCCESS".equals(response.getTradeStatus())){
					flag = true;
				}
			}
			
		} else {
			flag = false;
		}
		return flag;
	}
	
	 /**
	 * @Package com.sayimo.maker.commons.ali.pay
	 * @Author 龚益君
	 * @MethodName:alipayTradeRefund
	 * @Description:支付宝退款
	 * @param out_trade_no 商户订单号
	 * @param refund_amount 退款金额
	 * @return
	 * @throws AlipayApiException
	 * @Date 2017年3月7日,上午9:10:17
	 */
	public static String alipayTradeRefund(String out_trade_no,String refund_amount) throws AlipayApiException{
		AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
				AlipayUtil.ALIPAY_APPID,AlipayUtil.APP_PRIVATE_KEY,"json",AlipayConstants.CHARSET_UTF8,
				AlipayUtil.ALIPAY_PUBLIC_KEY,"RSA2");
		AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest(); // 支付宝退款请求
		 // 只需要传入业务参数
        Map<String, Object> param = new HashMap<>();
        param.put("out_trade_no", out_trade_no); // 商户订单号
        param.put("refund_amount", refund_amount);// 退款金额
		alipayRequest.setBizContent(JSON.toJSONString(param));
		AlipayTradeRefundResponse response = alipayClient.execute(alipayRequest);
		logger.info("response = " + JSONUtil.writeValueAsString(response));
		if(response.isSuccess()){
			if ("10000".equals(response.getCode())) {
				System.out.println(response.getCode());
				return Constant.SUCCESS;
			}
			
		} 
		return Constant.ERROR;
	}
	/**
	 * 
	 * @MethodName callbackALiPay
	 * @Description 支付宝回调验证签名是否正确
	 * 
	 * @author wangyang
	 * @date 2017年3月3日 上午10:11:22
	 * @param 
	 * @return Map
	 * @throws AlipayApiException 
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Map<String, Object> callbackALiPay(HttpServletRequest request)throws Exception {
		Map<String,Object> resultMap = new HashMap<String,Object>();
		try {
	        Map<String, String> callbackParamMap = RequestUtils.bindRequestParam(request, HashMap.class);
	        callbackParamMap.remove("sing");
	        callbackParamMap.remove("sign_type");
	        logger.info("callbackParamMap = " + callbackParamMap);
	        logger.info("callbackParamMap = " + JSONUtil.writeValueAsString(callbackParamMap));
	        
	        boolean signVerified = AlipaySignature.rsaCheckV1(callbackParamMap, AlipayUtil.ALIPAY_PUBLIC_KEY,
	                AlipayConstants.CHARSET_UTF8,"RSA2"); // 校验签名是否正确
	        
	        if (!signVerified) {
	        	resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
	        	return resultMap;
			}
	        logger.info("支付宝支付回调验签，  signVerified = " + signVerified);
	        
	        if(!"TRADE_SUCCESS".equals(callbackParamMap.get("trade_status")) 
	        		&& !"TRADE_FINISHED".equals(callbackParamMap.get("trade_status"))){
	        	resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
	        	return resultMap;
	        }
	        
	        if (callbackParamMap.containsKey("passback_params")) {
	        	String passbackParams = callbackParamMap.get("passback_params");
	        	passbackParams = URLDecoder.decode(URLDecoder.decode(passbackParams, "UTF-8"), "UTF-8");
	        	logger.info("passbackParams = " + passbackParams);
	        	Map passBackMap = JSONUtil.readValueToMap(passbackParams);
	        	resultMap.putAll(passBackMap);
			}
	        
	        logger.info("callbackParamMap = " + callbackParamMap);
	        logger.info("callbackParamMap = " + JSONUtil.writeValueAsString(callbackParamMap));
	        
	        String appId = callbackParamMap.get("app_id"), seller_id = callbackParamMap.get("seller_id"),
	        		_payPrice = String.valueOf(resultMap.get("_payPrice"));
	        		
	        
	        Double jPayPrice = Double.valueOf(_payPrice),
	        		total_amount = Double.valueOf(String.valueOf(callbackParamMap.get("total_amount")));
	        logger.info("total_amount = " + total_amount + ", _payPrice = " + _payPrice);
	        
	        // 比对金额
	        if (!resultMap.containsKey("_payPrice") || jPayPrice.compareTo(total_amount) != 0) {
	        	resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
	        	return resultMap;
			}
	        
	        // 不是支付宝回调（参数被篡改）
	        if(!AlipayUtil.ALIPAY_APPID.equals(appId) || !AlipayUtil.ALIPAY_SELLERID.equals(seller_id)){
	        	resultMap.put(Constant.STATUS, Constant.STATUS_ERROR);
	        	return resultMap;
	       	}
	        
	        resultMap.put(Constant.STATUS, Constant.STATUS_SUCCESS);
	        resultMap.put("ordersNo", callbackParamMap.get("out_trade_no")); // 订单号
	        logger.info("resultMap = " + resultMap);
	        return resultMap;
		}catch (Exception e) {
			e.printStackTrace();
			throw new ServiceException(e);
		}
	}
}
